package org.jspecial.tools;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DefaultPreprocessor implements Preprocessor {
    private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+(.*\\.(.*?))\\s*;");
    private static final Pattern INHERITANCE_PATTERN = Pattern.compile("class\\s+\\S+\\s+extends\\s+(\\S+)\\s*\\{");
    private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+\\S+\\s*\\{");
    private static final Pattern SUPER_CONSTRUCTOR_CALL_PATTERN = Pattern.compile("([^\\w\\.])super.(\\w+)(\\s*?)\\(");
    private static final Pattern SUPER_CALL_PATTERN = Pattern.compile("([^\\w\\.])super(\\s*?)\\(");

    /**
     * @inheritDoc
     */
    public String process(String script, String className) {
        String packageName = className.substring(0, className.lastIndexOf('.'));
        String simpleName = className.substring(packageName.length() + 1);
        String superClassName = null;

        script = IMPORT_PATTERN.matcher(script).replaceAll("var $2 = $1;");
        List<String> groups = new LinkedList<>();
        script = replaceAll(script, INHERITANCE_PATTERN, simpleName + "['class']['super'] = $1['class']; " + simpleName + "['class'].body = function() {", groups);
        if (!groups.isEmpty()) {
            superClassName = groups.get(0);
            int i = superClassName.lastIndexOf('.');
            if (i > 0) {
                superClassName = superClassName.substring(i);
            }
        }
        script = CLASS_PATTERN.matcher(script).replaceAll(simpleName + "['class'].body = function() {");
        if (superClassName != null) {
            script = SUPER_CONSTRUCTOR_CALL_PATTERN.matcher(script).replaceAll("$1this[\'___" + superClassName + "_$2\']$3(");
            script = SUPER_CALL_PATTERN.matcher(script).replaceAll("$1this[\'" + superClassName + "\']$2(");
        }
        Configuration cfg = new Configuration();
        cfg.setClassForTemplateLoading(getClass(), "/META-INF/combiner");
        try {
            Template template = cfg.getTemplate("class.ftl");
            Map<String, String> params = new HashMap<>();
            params.put("packageName", packageName);
            params.put("className", className);
            params.put("simpleClassName", simpleName);
            params.put("superClassName", superClassName);
            params.put("script", script);
            StringWriter out = new StringWriter();
            template.process(params, out);
            return out.toString();
        } catch (IOException | TemplateException e) {
            throw new RuntimeException(e);
        }
    }

    private static String replaceAll(String text, Pattern pattern, String replacemenet, List<String> groups) {
        Matcher m = pattern.matcher(text);
        m.reset();
        boolean result = m.find();
        if (result) {
            StringBuffer sb = new StringBuffer();
            do {
                for (int group = 1, n = m.groupCount(); group <= n; group++) {
                    groups.add(m.group(group));
                }
                m.appendReplacement(sb, replacemenet);
                result = m.find();
            } while (result);
            m.appendTail(sb);
            return sb.toString();
        }
        return text;
    }
}
